
import 'dart:io';
import 'dart:math';

import 'package:dio/dio.dart';
import 'package:dio/io.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'dart:convert' as convert;

import 'package:mkit/dio/Ssl_config.dart';
import 'package:mkit/dio/dio_exception.dart';
import 'package:mkit/dio/network_info.dart';


abstract class BaseDioProvider {
  String getBaseUrl();
  bool responseOk(Response r);
  int getErrorCode(Response r);
  String getErrorMessage(Response r);
  int getConnectTimeouts(); // set connect timeout 10s
  int getReceiveTimeouts(); // set receive timeout 10s
  Map<String, dynamic> getHeader();// set header  /// {"content-type": "application/json;charset=UTF-8","Accept": "application/json,text/plain,*/*"};
}


class DioHelper{

  static DioHelper? _instance;

  final Dio _dio;
  final BaseDioProvider _baseUrlProvider; // 实例变量，仅实例方法可访问

  final NetworkInfo _networkInfo = NetworkInfo();

  //  私有构造函数：仅内部可实例化
  DioHelper._internal(this._baseUrlProvider) : _dio = Dio() ;

  // 工厂构造函数：确保单例，完全隔离静态与实例上下文
  factory DioHelper({required BaseDioProvider baseUrlProvider}) {
    _instance ??= DioHelper._internal(baseUrlProvider);
    return _instance!;
  }

  //  保留 instance 调用方式，但改为实例 getter（非静态）
  static DioHelper get instance {
    if (_instance == null) {
      throw Exception("DioHelper 未初始化，请先调用 DioHelper()");
    }
    return _instance!;
  }

  // 存储当前活跃请求的取消令牌（可选，用于批量取消）
  final Map<String, CancelToken> _cancelTokens = {};

  // 生成请求唯一标识（用于绑定取消令牌）
  String _generateRequestId() => '${DateTime.now().microsecondsSinceEpoch}_${Random().nextInt(1000)}';

  // 取消单个请求
  void cancelRequest(String requestId, {String reason = "请求被取消"}) {
    if (_cancelTokens.containsKey(requestId)) {
      _cancelTokens[requestId]?.cancel(reason);
      _cancelTokens.remove(requestId);
    }
  }

  // 取消所有请求
  void cancelAllRequests({String reason = "所有请求被取消"}) {
    _cancelTokens.forEach((id, token) => token.cancel(reason));
    _cancelTokens.clear();
  }

  Future<T> get<T>(String url,{
    Map<String, dynamic>? data,
    Map<String, dynamic>? header,
    Map<String, dynamic>? addHeader,
    FormData? formData,
    SslConfig? sslConfig,
    int? connectTimeout,
    int? receiveTimeout,
    CancelToken? cancelToken}) async {
    // 数据拼接
    if (data != null && data.isNotEmpty) {
      StringBuffer options = StringBuffer('?');
      data.forEach((key, value) {
        options.write('$key=$value&');
      });
      String optionsStr = options.toString();
      optionsStr = optionsStr.substring(0, optionsStr.length - 1);
      url += optionsStr;
    }
    final requestId = _generateRequestId();
    final token = cancelToken ?? CancelToken(); // 优先使用外部传入的令牌
    _cancelTokens[requestId] = token;
    try{
      return _sendRequest(url,MethodName.get,
          data:data,
          header:header,
          addHeader:addHeader,
          formData:formData,
          sslConfig:sslConfig,
          connectTimeout:connectTimeout,
          receiveTimeout:receiveTimeout,
          cancelToken: token
      );
    }finally{
      _cancelTokens.remove(requestId); // 请求结束后移除
    }
  }

  Future<T>  post<T>(String url, {
    Map<String, dynamic>? data,
    Map<String, dynamic>? header,
    Map<String, dynamic>? addHeader,
    FormData? formData,
    SslConfig? sslConfig,
    int? connectTimeout,
    int? receiveTimeout,
    CancelToken? cancelToken}){
    final requestId = _generateRequestId();
    final token = cancelToken ?? CancelToken(); // 优先使用外部传入的令牌
    _cancelTokens[requestId] = token;
    try{
      return _sendRequest(url,MethodName.post,
          data:data,
          header:header,
          addHeader:addHeader,
          formData:formData,
          sslConfig:sslConfig,
          connectTimeout:connectTimeout,
          receiveTimeout:receiveTimeout,
          cancelToken: token
      );
    }finally{
      _cancelTokens.remove(requestId); // 请求结束后移除
    }

  }

  Future<T> put<T>(String url,{
    Map<String, dynamic>? data,
    Map<String, dynamic>? header,
    Map<String, dynamic>? addHeader,
    FormData? formData,
    SslConfig? sslConfig,
    int? connectTimeout,
    int? receiveTimeout,
    CancelToken? cancelToken}){
    // 数据拼接
    if (data != null && data.isNotEmpty) {
      StringBuffer options = StringBuffer('?');
      data.forEach((key, value) {
        options.write('$key=$value&');
      });
      String optionsStr = options.toString();
      optionsStr = optionsStr.substring(0, optionsStr.length - 1);
      url += optionsStr;
    }
    final requestId = _generateRequestId();
    final token = cancelToken ?? CancelToken(); // 优先使用外部传入的令牌
    _cancelTokens[requestId] = token;
    try{
      return _sendRequest(url,MethodName.put,
          data:data,
          header:header,
          addHeader:addHeader,
          formData:formData,
          sslConfig:sslConfig,
          connectTimeout:connectTimeout,
          receiveTimeout:receiveTimeout,
          cancelToken: token
      );
    }finally{
      _cancelTokens.remove(requestId); // 请求结束后移除
    }
  }

  Future<T> delete<T>(String url,{
    Map<String, dynamic>? data,
    Map<String, dynamic>? header,
    Map<String, dynamic>? addHeader,
    FormData? formData,
    SslConfig? sslConfig,
    int? connectTimeout,
    int? receiveTimeout,
    CancelToken? cancelToken}){
    // 数据拼接
    if (data != null && data.isNotEmpty) {
      StringBuffer options = StringBuffer('?');
      data.forEach((key, value) {
        options.write('$key=$value&');
      });
      String optionsStr = options.toString();
      optionsStr = optionsStr.substring(0, optionsStr.length - 1);
      url += optionsStr;
    }
    final requestId = _generateRequestId();
    final token = cancelToken ?? CancelToken(); // 优先使用外部传入的令牌
    _cancelTokens[requestId] = token;
    try{
      return _sendRequest(url,MethodName.delete,
          data:data,
          header:header,
          addHeader:addHeader,
          formData:formData,
          sslConfig:sslConfig,
          connectTimeout:connectTimeout,
          receiveTimeout:receiveTimeout,
          cancelToken: token
      );
    }finally{
      _cancelTokens.remove(requestId); // 请求结束后移除
    }
  }

  Future<T> _sendRequest<T>(String url,String method, {
    Map<String, dynamic>? data,
    Map<String, dynamic>? header,
    Map<String, dynamic>? addHeader,
    FormData? formData,
    SslConfig? sslConfig,
    int? connectTimeout,
    int? receiveTimeout,
    required CancelToken cancelToken,
  }) async {
    if(! await _networkInfo.isConnected()){
      throw NetworkException(ErrorCode.CODE_NO_NETWORK, "无网络连接");
    }
    if (!url.startsWith('http')) {
      String baseUrl = _baseUrlProvider.getBaseUrl();
      if(! baseUrl.startsWith('http')){
        throw OtherException(ErrorCode.CODE_UNDEFINED_BASE_URL, 'undefined base url');
      }
      url = baseUrl + url;
    }

    try {
      Map<String, dynamic> dataMap = data ?? <String, dynamic>{};
      Map<String, dynamic>? headersMap = header?? _baseUrlProvider.getHeader();
      if(addHeader != null){
        headersMap.addAll(addHeader);
      }
      // 打印请求头
      debugPrint('请求头：$headersMap');

      // 配置dio请求信息
      Response response;

      // 配置 SSL（抽离为方法）
      if (sslConfig != null && url.startsWith('https')) {
        await _configureSsl(sslConfig);
      }

      _dio.options.connectTimeout =  Duration(milliseconds: connectTimeout??_baseUrlProvider.getConnectTimeouts()); // 服务器链接超时，毫秒
      _dio.options.receiveTimeout =  Duration(milliseconds: receiveTimeout??_baseUrlProvider.getReceiveTimeouts()); // 响应流上前后两次接受到数据的间隔，毫秒
      _dio.options.headers.clear();
      _dio.options.headers.addAll(headersMap); // 添加headers,如需设置统一的headers信息也可在此添加

      ///访问地址打印
      debugPrint('请求地址：$url');
      debugPrint('请求内容：formData:${convert.jsonEncode(formData).toString()}\n请求内容：dataMap:${convert.jsonEncode(dataMap).toString()}');
      response = await _dio.request(
        url,
        options: Options(
          method: method, // 通过 Options 传递 method（GET/POST等）
        ),
        data: formData ?? dataMap, // 请求体数据
        cancelToken: cancelToken, // 取消令牌
      );

      if(response.statusCode != null){
        /// 200（含） - 300(不含)  表示正常响应
        if (response.statusCode! >= 300 || response.statusCode! < 200 ) {
          throw HttpStatusException(response.statusCode!, _httpError(response.statusCode!));
        }

        if(response.data==null){
          throw NetworkException(ErrorCode.CODE_RESPONSE_DATA_IS_NULL, "response data is null");
        }else{
           debugPrint('返回内容Json：${convert.jsonEncode(response.data).toString()}');

          // 按请求类型处理
           if(_baseUrlProvider.responseOk(response)){
             return response.data;
           }else{
             throw DataException(_baseUrlProvider.getErrorCode(response), _baseUrlProvider.getErrorMessage(response));
           }
        }
      }else{
        throw NetworkException(ErrorCode.CODE_RESPONSE_STATUS_CODE_IS_NULL, "服务器错误");
      }
    }on DioException catch (err){
      if(err.response==null){
        throw NetworkException(ErrorCode.CODE_UNKNOWN_ERROR, "服务器无响应");
      }else if (err.type == DioExceptionType.connectionTimeout) {
        throw NetworkException(ErrorCode.CODE_CONNECT_TIMEOUT, "网络连接超时");
      } else if (err.type == DioExceptionType.badResponse){
        throw HttpStatusException( err.response!.statusCode!, _httpError(err.response!.statusCode!));
      }else if (err.type == DioExceptionType.cancel) {
        throw CancelException(ErrorCode.CODE_CANCEL,err.message ?? "请求已取消"); // 自定义取消异常
      }
      else{
        throw OtherException(ErrorCode.CODE_OTHER_ERROR, "其他错误");
      }
    }

  }


  // http 错误在这里补充
  String _httpError(int? code) {
    code ??= 0;
    switch(code){
      case 401:
        return "未授权";

      case 404:
        return "404 NOT FOUND";

      case 500:
        return "系统异常";

      default:
        return "其他HTTP错误，错误码：$code";
    }
  }

  Future<void> _configureSsl(SslConfig sslConfig) async {
    if(sslConfig.reCer){
      if(sslConfig.severCert == null || sslConfig.severCert == ''){
        throw SslException(ErrorCode.CODE_UNDEFINED_SEVER_CERT,'severCert undefined');
      }
      if(sslConfig.clientCert == null || sslConfig.clientCert == ''){
        throw SslException(ErrorCode.CODE_UNDEFINED_CLIENT_CERT,'clientCert undefined');
      }
      if(sslConfig.clientPrivate == null || sslConfig.clientPrivate == ''){
        throw SslException(ErrorCode.CODE_UNDEFINED_CLIENT_PRIVATE,'clientPrivate undefined');
      }
      if(sslConfig.certPassword == null || sslConfig.certPassword == ''){
        throw SslException(ErrorCode.CODE_UNDEFINED_CERT_PASSWORD,'certPassword undefined');
      }

      List<int> caData = (await rootBundle.load(sslConfig.severCert!)).buffer.asUint8List();
      List<int> clientCertData = (await rootBundle.load(sslConfig.clientCert!)).buffer.asUint8List();
      List<int> clientPrivateData = (await rootBundle.load(sslConfig.clientPrivate!)).buffer.asUint8List();
      (_dio.httpClientAdapter as IOHttpClientAdapter).onHttpClientCreate  = (client) {
        SecurityContext sc = SecurityContext(withTrustedRoots: true);
        sc.setTrustedCertificatesBytes(caData); //设置服务器端的公钥，为了验证服务器端
        sc.useCertificateChainBytes(clientCertData);//设置客户端证书到证书链
        sc.usePrivateKeyBytes(clientPrivateData, password:sslConfig.certPassword);//设置私钥 虽然源码里的注释说pem不用密码，但是这里必须
        HttpClient httpClient = HttpClient(context: sc);
        httpClient.badCertificateCallback=(X509Certificate cert, String host, int port){
          return true;
        };
        return httpClient;
      };
    }else{
      (_dio.httpClientAdapter as IOHttpClientAdapter).onHttpClientCreate = (HttpClient client){
        client.badCertificateCallback = (X509Certificate cert ,String host ,int port){
          return true;
        };
      };
    }
  }

}


/// 一些错误码
class ErrorCode{
  /// -201  到  -299
  static const int CODE_UNDEFINED_BASE_URL = -201;
  static const int CODE_UNDEFINED_SEVER_CERT = -202;
  static const int CODE_UNDEFINED_CLIENT_CERT = -203;
  static const int CODE_UNDEFINED_CLIENT_PRIVATE = -204;
  static const int CODE_UNDEFINED_CERT_PASSWORD = -205;
  static const int CODE_HTTP_METHOD_NOT_FOUND = -206;
  static const int CODE_RESPONSE_DATA_IS_NULL = -207;
  static const int CODE_RESPONSE_STATUS_CODE_IS_NULL = -208;
  static const int CODE_UNKNOWN_ERROR = -209;
  static const int CODE_CONNECT_TIMEOUT = -210;
  static const int CODE_OTHER_ERROR = -211;
  static const int CODE_NO_NETWORK = -220; // 无网络错误码
  static const int CODE_CANCEL =-251;
}


/// 请求方法名称
class MethodName{
  static const String get = 'GET';
  static const String post = 'POST';
  static const String put = 'PUT';
  static const String delete = 'DELETE';
}


